package com.admin.oauth.component;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.admin.component.redis.RedisUtils;
import com.admin.core.dto.cache.ParamConfigCacheBO;
import com.admin.oauth.constant.CacheConstant;
import com.admin.oauth.dto.LoginUserDto;
import com.admin.oauth.dto.OnlineUserDto;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author sq-lan
 **/
@Component
public class UserCacheManager {

    @Resource
    private RedisUtils redisUtils;

    /**
     * 返回用户缓存
     *
     * @param tokenMd5 tokenMd5
     * @return JwtUserDto
     */
    public LoginUserDto getJwtUser(String tokenMd5) {
        if (StringUtils.isEmpty(tokenMd5)) {
            return null;
        }
        // 获取数据
        Object obj = redisUtils.get(getLoginUserDetailKey(tokenMd5));
        if (obj != null) {
            return (LoginUserDto) obj;
        }
        return null;
    }

    /**
     * 添加缓存到Redis
     *
     * @param token 用户名
     */
    public void set(String token, LoginUserDto jwtUser, OnlineUserDto onlineUser) {
        if (StringUtils.isEmpty(token)) {
            return;
        }
        String md5 = tokenToMd5(token);
        // 添加随机时间, 避免数据同时过期
        long time = ParamConfigCacheBO.loginParamConfig.getTokenExpire() + RandomUtil.randomInt(900, 1800);
        redisUtils.set(getLoginUserDetailKey(md5), jwtUser, time);
        redisUtils.set(getOnlineUserKey(md5, onlineUser.getUserName()), onlineUser, time);
    }

    /**
     * 续期
     *
     * @param tokenMd5 令牌md5
     */
    public void renewal(String tokenMd5, String username) {
        // 减少访问 redis 次数 todo
        // 生成token 默认添加存redis的过期时间，续期时先验证token的过期时间，如果验证通过(即未续期情况)且不在续期检查范围内，则不续期。
        // Claims claims = Jwts.parserBuilder().setSigningKey(tokenSignKey).parseClaimsJws(tokenMd5).getBody();
        // claims.getExpiration();

        //如果验证通过，在续期检查范围内，直接续期

        // 过期的token验证肯定不通过。
        String loginUserDetailKey = getLoginUserDetailKey(tokenMd5);
        long time = redisUtils.getExpire(loginUserDetailKey);
        // 如果在续期检查范围内，则续期
        if (time > 1 && time < ParamConfigCacheBO.loginParamConfig.getRenewScope()) {
            redisUtils.expire(loginUserDetailKey, ParamConfigCacheBO.loginParamConfig.getRenewal());
            redisUtils.expire(getOnlineUserKey(tokenMd5, username), ParamConfigCacheBO.loginParamConfig.getRenewal());
        }
    }

    public void logout(String token) {
        String md5 = tokenToMd5(token);
        LoginUserDto jwtUser = getJwtUser(md5);
        if (jwtUser == null) {
            return;
        }
        redisUtils.del(getLoginUserDetailKey(md5));
        redisUtils.del(getOnlineUserKey(md5, jwtUser.getUsername()));
    }

    public void kickOut(String username) {
        String pattern = CacheConstant.CACHE_KEY_ONLINE_USER + username + "*";
        List<String> delKeys = redisUtils.scanDel(pattern);
        Set<Object> keys = new HashSet<>(delKeys.size());
        for (String entry : delKeys) {
            String token = entry.substring(entry.lastIndexOf("-") + 1);
            keys.add(getLoginUserDetailKey(token));
        }
        redisUtils.delByKeys(keys);
    }

    public String getLoginUserDetailKey(String tokenMd5) {
        return CacheConstant.CACHE_KEY_LOGIN_USER_DETAIL + tokenMd5;
    }

    public String getOnlineUserKey(String tokenMd5, String username) {
        return CacheConstant.CACHE_KEY_ONLINE_USER + username + "-" + tokenMd5;
    }

    public String tokenToMd5(String token) {
        return DigestUtil.md5Hex(token);
    }

}
